#pragma once
#include "ofMain.h"
#include "ofxBox2d.h"

#ifndef _MSC_VER
#include "ofxFlashCommunication.h"
// exclude flash. ofxFlashCommuncation does not build on visual studio.
#define FLASHCOMM
#endif

//#undef FLASHCOMM

#include "ofxOsc.h"

// -------------------------------------------------

// Box2D collision filter bits for different game objects
enum GameCollisionBits {
	COL_BOUNDARY = 1,
	COL_BULLET = 2,
	COL_TURRET = 4,
	COL_LINK = 8,
	COL_PLAYER = 16,
	
	COL_ALL = 31
};

inline void SetBodyCollision(b2Body* b, int category, int collideAgainst)
{
	b2Filter filter;
	
	//filter.
	filter.categoryBits = category;
	filter.maskBits = collideAgainst;
	
	b->GetFixtureList()->SetFilterData(filter);
}

template<typename T>
void DeleteAllElems(T& container) {
	for(typename T::iterator i=container.begin();i!=container.end();++i)
		delete *i;
	container.clear();
}

struct GameObject {
	enum ObjectType {
		Link,
		Player,
		Turret,
		Bullet
	} objType;

	GameObject(ObjectType objType) : objType (objType) {}
};


struct LinkObject : public GameObject {
	LinkObject() : GameObject(GameObject::Link) {}
	
	ofxBox2dRect rect;
	ofImage image;
	string url;
};

struct Bullet : public GameObject {
	Bullet() : GameObject(GameObject::Bullet) {
		startTime = 0; radius=10;
		asyncLoadID = 0;
		state = Outside;
	}
	~Bullet() {
		shape.destroy();
	}

	ofxBox2dPolygon shape;
	void draw();
	void setup(ofxBox2d* box2d, ofVec2f pos, ofVec2f vel);
	ofVec2f getPosition() { return shape.getPosition (); }
	void setPosition(ofVec2f pos) { shape.setPosition(pos); }
	void setHitBoundaryMode(bool hitBoundary);
	
	float radius;
	float startTime;
	
	ofMutex imageMutex;
	ofImage image;
	string keyword, imageURL;
	int asyncLoadID;
	
	enum State {
		Outside,
		BeingFired,
		Inside,
		Disappearing
	} state;
};

struct Player : public GameObject {

	Player() : GameObject(GameObject::Player) {}
	void draw();
	void load(const string& url);
	void setup(ofxBox2d* box2d, ofVec2f pos);

	ofVec2f getPosition() { return circle.getPosition(); }
	
	ofxBox2dCircle circle;
};


class TestApp : public ofBaseApp, public b2ContactListener {
public:

	TestApp();
	
	void loadStartupData();
	
	void setup();
	void update();
	void draw();
	void exit();
	
	void keyPressed(int key);
	void keyReleased(int key);
	void mouseMoved(int x, int y);
	void mouseDragged(int x, int y, int button);
	void mousePressed(int x, int y, int button);
	void mouseReleased(int x, int y, int button);
	void resized(int w, int h);

	// b2ContactListener implementation
	void BeginContact(b2Contact* contact);
	void EndContact(b2Contact* contact);

	ofxBox2d box2d;			  //	the box2d world

	Player player;
	ofLight playerLight;
//	ofSpherePrimitive bulletShape;
		
	// ----------------------------------------------------------- bullets
	vector<Bullet*> outsideBullets;
	vector<Bullet*> bullets;
	void drawBullet(Bullet* b);
	void fireBullet();
	void addOutsideBullet(const string& url, const string& keyword);
	void updateBullets();
	
	void setupLighting();
	
	vector<LinkObject*> linkObjects;
	void drawLinkObjects();
	void initLinkObjects();

	ofVec2f clickPos;

	// ---------------------------------------------------------- flash connection	
	ofxOscSender oscSender;
	ofxOscReceiver oscReceiver;
#ifdef FLASHCOMM
	ofxFlashCommunication* flash;
#endif
	void parseFlashMsg(const string& s);
	void urlResponse(ofHttpResponse &httpResponse);

	// ---------------------------------------------------------- arduino
	void arduinoUpdate();
	ofSerial arduino;
	bool arduinoEnabled;
	vector<ofSerialDeviceInfo> serialDevices;
	int selectedSerial;
	ofVec2f controlSliderValue;
	
	// ---------------------------------------------------------- turret
	ofPolyline turretBoundaryLine;
	bool movingTurret;
	float turretPosition; // position along boundary line (0-1)
	void drawTurret();
	ofVec2f computeTurretPosition(ofVec2f* normal=0);
	ofVec2f computeBulletFirePosition();

	list<string> messages;
	string buffer;
	void showMessageString(const string& s);

	// ---------------------------------------------------------- geometry
	void createBoundary();
	vector<ofVec2f> computeBoundary(float Roffset);
	void addGradientQuads(const vector<ofVec2f>& a,const vector<ofVec2f>& b, ofFloatColor colA, ofFloatColor colB);
	void drawPolyline(ofPolyline& pl);

	vector<b2PolygonShape*> staticPoly;
	b2Body* staticPolyBody;

	struct StaticMesh {
		ofVbo vbo;
		int mode;
		int numVerts;
	};
	vector<StaticMesh*> staticGeom;
	
	// ---------------------------------------------------------- shaders
	ofShader backgroundShader;
	void drawBackground();

    // ========================================================== fixed column shadows
	void drawFixedColumnShadows();
	vector<ofxBox2dCircle*> fixedCircles;
	void initFixedCircles();
	void addFixedCircle(ofVec2f pos);
	bool drawFixedCircles;
    void drawAimingLine();
};

